﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using FileDownloadLib;

namespace FileDownload
{
    public sealed class DayanwenxueDownloadImagesText : BaseDownloadImagesText
    {
        private const string HttpMainHost = "www.dayanwenxue.com";

        private const string HttpImageHost = "";

        private const string PageContentNotExist = "<div class=\"divimage\">Image Not Exist [{0}]</div>";
        private const string PageContentLine = "<div class=\"divimage\"><img src=\"{0}\" border=\"0\" class=\"imagecontent\"></div>";
        private const string IndexContentLine = "<dd><a href=\"{0}\" title=\"{1}\">{2}</a></dd>";



        public DayanwenxueDownloadImagesText(string indexUri, string saveToRoot, string novelTitle, bool UpdateMode, int MaxPages)
        {
            mUpdateMode = UpdateMode;
            mNovelTitle = novelTitle;

            DownloadConfig.Protocol = "http";
            mIndexUri = indexUri;
            mSaveToRoot = string.Format("{0}\\{1}", saveToRoot, novelTitle);
            mSaveToImageRoot = string.Format("{0}\\Images", mSaveToRoot);

            mNovelRoot = mSaveToRoot;

            mMaxPages = MaxPages;

            htmlIndexTemplate = string.Format("{0}\\HTTPTemplateIndex.html", AppDomain.CurrentDomain.BaseDirectory);
            htmlPageTemplate = string.Format("{0}\\HTMLTemplatePages.html", AppDomain.CurrentDomain.BaseDirectory);

            CurrentUtl = string.Format(@"{0}/{1}", HttpMainHost, indexUri);
        }

        private string CurrentUtl = string.Empty;

        #region download One by One
        public override void ProcessAllOneByOne()
        {
            stop = false;

            List<PageInfo> pageInfoList = new List<PageInfo>();
            List<IndexContent> pageIndexList = new List<IndexContent>();
            Encoding encoding = null;

            try
            {
                FireOnProgressChangedEvent(0, 0, ProgressEnum.Progressing);

                if (ProcessToGetPageInfosOneByOne(ref pageInfoList, ref pageIndexList, ref encoding))
                {
                    //DownloadImages(pageInfoList, !mUpdateMode);
                    BuildIndex(pageIndexList, encoding, mNovelTitle);
                }
            }
            catch (Exception ex)
            {
                Logger.Instance.Error(ex.ToString());
                FireOnProgressChangedEvent(pageIndexList.Count, pageIndexList.Count, ProgressEnum.FinishFailed);
                return;
            }

            FireOnProgressChangedEvent(pageIndexList.Count, pageIndexList.Count, ProgressEnum.FinishOK);
        }

        public override void ProcessAll()
        {
        }

        private bool ProcessToGetPageInfosOneByOne(ref List<PageInfo> pageInfoList, ref List<IndexContent> indexList, ref Encoding encoding)
        {
            int count = 0;

            pageInfoList = new List<PageInfo>();

            if (ProcessIndex(ref indexList, ref encoding))
            {
                if (indexList != null)
                {
                    Logger.Instance.Info("Get Page Infor List OK!");

                    FireOnProgressChangedEvent(indexList.Count, 0, ProgressEnum.Progressing);

                    if (!Directory.Exists(mSaveToRoot))
                    {
                        Directory.CreateDirectory(mSaveToRoot);
                    }

                    StreamReader SR = new StreamReader(new FileStream(htmlPageTemplate, FileMode.Open, FileAccess.Read));

                    string tempContent = SR.ReadToEnd();

                    SR.Close();

                    if (encoding == null)
                    {
                        encoding = Encoding.UTF8;
                    }


                    foreach (IndexContent indexContent in indexList)
                    {
                        if (stop)
                        {
                            break;
                        }

                        count++;
                        FireOnProgressChangedEvent(indexList.Count, count, ProgressEnum.Progressing);

                        if (count > mMaxPages)
                        {
                            Logger.Instance.InfoImportant("Reach Max Pages: " + mMaxPages);
                            break;
                        }

                        Logger.Instance.Info("Processing Record: " + count);


                        string file = string.Format("{0}/{1}", mSaveToRoot, indexContent.FileName);

                        if (File.Exists(file) && mUpdateMode)
                        {
                            Logger.Instance.InfoImportant("Update Mode File Exists: " + new FileInfo(file).Name);
                            continue;
                        }

                        PageInfo pi = null;
                        if (ProcessOnePage(indexContent.Url, indexContent, ref pi))
                        {
                            if (pi != null && pi.ValidPageInfor())
                            {
                                pageInfoList.Add(pi);
                                DownloadImages(pi, !mUpdateMode);
                                BuildPage(pi, tempContent, encoding);
                            }
                        } 
                    }

                    
                    return true;
                }
            }

            Logger.Instance.Info("Get Page Infor List Failed!");
            return false;
        }

        private bool DownloadImages(PageInfo pi, bool overwrite)
        {
            try
            {
                if (!Directory.Exists(mSaveToImageRoot))
                {
                    Directory.CreateDirectory(mSaveToImageRoot);
                }

                int piCount = 0;
                int imgCount = 0;

                foreach (string image in pi.ImageList)
                {
                    string FileName = image.Substring(image.LastIndexOf("/") + 1,
                        (image.Length - image.LastIndexOf("/") - 1));

                    string saveToLoc = string.Format("{0}\\{1}", mSaveToImageRoot, FileName);

                    if (File.Exists(saveToLoc) && overwrite)
                    {
                        Logger.Instance.InfoImportant("Over write mode, Delete: " + new FileInfo(saveToLoc).Name);
                        File.Delete(saveToLoc);
                    }
                    else if (File.Exists(saveToLoc) && !overwrite)
                    {
                        Logger.Instance.InfoImportant("Update Mode, File Exists: " + new FileInfo(saveToLoc).Name);
                        pi.DownloadedImageList.Add(FileName);
                        continue;
                    }

                    if (FileDownloadUtil.DownloadFile(image, saveToLoc))
                    {
                        Logger.Instance.Info(string.Format("Dwonload OK: {0}", image));
                        pi.DownloadedImageList.Add(FileName);

                        imgCount++;
                    }
                    else
                    {
                        Logger.Instance.Info(string.Format("Dwonload Failed: {0}", image));
                    }

                    piCount++;
                    Logger.Instance.Info("Download Image For PI: " + piCount);
                    Logger.Instance.Info("Download Image: " + imgCount);
                }


                return true;
            }
            catch (Exception ex)
            {
                Logger.Instance.Error(ex.ToString());
                return false;
            }
        }

        private bool BuildPages(List<PageInfo> pageInfoList, Encoding encoding)
        {
            try
            {
                if (!Directory.Exists(mSaveToRoot))
                {
                    Directory.CreateDirectory(mSaveToRoot);
                }

  
                StreamReader SR = new StreamReader(new FileStream(htmlPageTemplate, FileMode.Open, FileAccess.Read));

                string tempContent = SR.ReadToEnd();

                SR.Close();

                if (encoding == null)
                {
                    encoding = Encoding.UTF8;
                }

                foreach (PageInfo pi in pageInfoList)
                {
                    Logger.Instance.InfoImportant("Build PI: " + pi.readid);
                    BuildPage(pi, tempContent, encoding);
                }

                return true;
            }
            catch (Exception ex)
            {
                Logger.Instance.Error(ex.ToString());
                return false;
            }
        }

        private bool BuildPage(PageInfo pi, string pageContentTemplate, Encoding encoding)
        {
            try
            {
                StringBuilder indexStr = new StringBuilder();
                string tempContent = string.Empty;

                if (pi.ContentType == ContentTypeEnum.Image)
                {
                    Logger.Instance.InfoImportant("Image Mode");
                    foreach (string image in pi.DownloadedImageList)
                    {
                        if (File.Exists(string.Format("{0}\\{1}", mSaveToImageRoot, image)))
                        {
                            tempContent += string.Format(PageContentLine, string.Format("Images\\{0}", image));
                        }
                        else
                        {
                            tempContent += string.Format(PageContentNotExist, string.Format("Images\\{0}", image));
                        }
                    }
                }
                else if(pi.ContentType == ContentTypeEnum.Text)
                {
                    Logger.Instance.InfoImportant("Text Mode");
                    tempContent = pi.TextContent;
                }

                pageContentTemplate = pageContentTemplate.Replace(TAG_PAGE_CONTENT, tempContent);
                pageContentTemplate = pageContentTemplate.Replace(TAG_PREVIOUS_PAGE, pi.preview_page);
                pageContentTemplate = pageContentTemplate.Replace(TAG_NEXT_PAGE, pi.next_page);
                pageContentTemplate = pageContentTemplate.Replace(TAG_INDEX_PAGE, pi.index_page);
                pageContentTemplate = pageContentTemplate.Replace(TAG_BOOK_ID, pi.bookid);
                pageContentTemplate = pageContentTemplate.Replace(TAG_READ_ID, pi.readid);
                pageContentTemplate = pageContentTemplate.Replace(TAG_TITLE, pi.IndexContent.TitleContent);


                string file = string.Format("{0}\\{1}", mSaveToRoot, pi.IndexContent.FileName);

                if (File.Exists(file) && !mUpdateMode)
                {
                    Logger.Instance.InfoImportant("Over write mode, Delete: " + new FileInfo(file).Name);
                    File.Delete(file);
                }

                if (!File.Exists(file))
                {
                    Logger.Instance.InfoImportant("Create New File: " + new FileInfo(file).Name);
                    StreamWriter SW = new StreamWriter(new FileStream(file, FileMode.CreateNew, FileAccess.Write), encoding);

                    SW.Write(pageContentTemplate);
                    SW.Flush();
                    SW.Close();
                }
                else
                {
                    Logger.Instance.InfoImportant("File Exists Skip: " + file);
                }

                return true;
            }
            catch (Exception ex)
            {
                Logger.Instance.Error(ex.ToString());
                return false;
            }
        }

        private bool BuildIndex(List<IndexContent> pageIndexList, Encoding encoding, string book)
        {
            try
            {
                StringBuilder indexStr = new StringBuilder();
                foreach (IndexContent ic in pageIndexList)
                {
                    string appendstr = string.Format(IndexContentLine, ic.FileName, ic.Title, ic.TitleContent);
                    indexStr.AppendLine(appendstr);
                    Logger.Instance.InfoImportant(appendstr);
                }

                StreamReader SR = new StreamReader(new FileStream(htmlIndexTemplate, FileMode.Open, FileAccess.Read));

                string tempContent = SR.ReadToEnd();

                tempContent = tempContent.Replace(TAG_PAGE_CONTENT, indexStr.ToString());
                tempContent = tempContent.Replace(TAG_TITLE, book);

                string file = string.Format("{0}\\index.html", mSaveToRoot);

                if (File.Exists(file))
                {
                    File.Delete(file);
                }

                if (encoding == null)
                {
                    encoding = Encoding.UTF8;
                }

                StreamWriter SW = new StreamWriter(new FileStream(file, FileMode.CreateNew, FileAccess.Write), encoding);

                SW.Write(tempContent);
                SW.Flush();

                SR.Close();
                SW.Close();

                return true;
            }
            catch (Exception ex)
            {
                Logger.Instance.Error(ex.ToString());
                return false;
            }
        }

        private bool ProcessIndex(ref List<IndexContent> indexList, ref Encoding encoding)
        {
            string content = string.Empty;

            if (!FileDownloadUtil.DownloadWebPage(HttpMainHost, mIndexUri, ref content, ref encoding))
            {
                Logger.Instance.Error("Download Index Failed!");
                return false;
            }
            else
            {
                Logger.Instance.Info("Download Index OK!");

                indexList = GetIndex(content);

                return true;
            }
        }

        private bool ProcessOnePage(string url, IndexContent indexContent, ref PageInfo pi)
        {
            string content = string.Empty;
            Encoding encoding = null;

            if (!FileDownloadUtil.DownloadWebPage(CurrentUtl, url, ref content, ref encoding))
            {
                Logger.Instance.Error(string.Format("Download Page Failed! [{0}]", url));
                return false;
            }
            else
            {
                pi = GetPageInfo(content, indexContent);

                return true;
            }
        }

        private List<IndexContent> GetIndex(string htmlContent)
        {
            Logger.Instance.Info("Enter GetIndex");

            List<IndexContent> indexList = new List<IndexContent>();

            htmlContent = htmlContent.ToLower();
            htmlContent = htmlContent.Replace(System.Environment.NewLine, "");
            string pattern = @"<td class=""ccss""><a href=""(?<url>.*?)"">(?<titleContent>.*?)</a></td>";


            MatchCollection collections = Regex.Matches(htmlContent, pattern);

            foreach (Match match in collections)
            {
                IndexContent content = new IndexContent();
                content.Url = match.Groups["url"].Value;
                content.Title = match.Groups["titleContent"].Value;
                content.TitleContent = match.Groups["titleContent"].Value;
                content.LineContent = match.Value;

                if (!string.IsNullOrEmpty(content.Url))
                {
                    string FileName = content.Url.Substring(content.Url.LastIndexOf("/") + 1,
                        (content.Url.Length - content.Url.LastIndexOf("/") - 1));

                    if (!string.IsNullOrEmpty(FileName))
                    {
                        content.FileName = FileName;
                    }
                }


                indexList.Add(content);
            }

            Logger.Instance.Info("Exit GetIndex");

            return indexList;
        }

        private PageInfo GetPageInfo(string htmlContent, IndexContent indexContent)
        {
            Logger.Instance.Info("Enter GetPageInfo");

            PageInfo ps = new PageInfo();

            ps.IndexContent = indexContent;

            htmlContent = htmlContent.ToLower();
            string pattern_preview_page = @"var preview_page = ""(?<matchValue>.*?)"";";
            string pattern_next_page = @"var next_page = ""(?<matchValue>.*?)"";";
            string pattern_index_page = @"var index_page = ""(?<matchValue>.*?)"";";
            string pattern_bookid = @"var article_id = ""(?<matchValue>.*?)"";";
            string pattern_readid = @"var chapter_id = ""(?<matchValue>.*?)"";";

            string pattern_images = string.Format(@"<div class=""divimage""><img src=""(?<imageValue>{0}.*?)"" border=""0"" class=""imagecontent""></div>", HttpImageHost);
            string pattern_text = "<div id=\"content\">(?<textValue>((?!</div>)[\\s\\S])*)</div>";

            string pattern_Script = "<script>(?<scriptValue>[\\s\\S]*)</script>";


            Match match_preview_page = Regex.Match(htmlContent, pattern_preview_page);
            ps.preview_page = match_preview_page.Groups["matchValue"].Value;

            Match match_next_page = Regex.Match(htmlContent, pattern_next_page);
            ps.next_page = match_next_page.Groups["matchValue"].Value;

            Match match_index_page = Regex.Match(htmlContent, pattern_index_page);
            ps.index_page = match_index_page.Groups["matchValue"].Value;

            Match match_bookid = Regex.Match(htmlContent, pattern_bookid);
            ps.bookid = match_bookid.Groups["matchValue"].Value;

            Match match_readid = Regex.Match(htmlContent, pattern_readid);
            ps.readid = match_readid.Groups["matchValue"].Value;

            MatchCollection collections = Regex.Matches(htmlContent, pattern_images);

            if (collections.Count > 0)
            {
                ps.ContentType = ContentTypeEnum.Image;

                foreach (Match match in collections)
                {
                    string imageValue = match.Groups["imageValue"].Value;

                    if (!string.IsNullOrEmpty(imageValue))
                    {
                        ps.ImageList.Add(imageValue);
                    }
                }
            }
            else
            {
                Match match = Regex.Match(htmlContent, pattern_text);
                string textValue = match.Groups["textValue"].Value;

                if (!string.IsNullOrEmpty(textValue))
                {
                    match = Regex.Match(textValue, pattern_Script);
                    string scriptValue = match.Groups["scriptValue"].Value;

                    if (!string.IsNullOrEmpty(scriptValue))
                    {
                        textValue = textValue.Replace(scriptValue, "");
                    }

                    textValue = RemoveInvalidString(textValue);
                }

                ps.ContentType = ContentTypeEnum.Text;
                ps.TextContent = textValue;
            }

            Logger.Instance.Info("Exit GetPageInfo");

            return ps;
        }

        #endregion
    }
}
